﻿using System;
using Chip8.Core.Bus;

namespace Chip8.Core.Processor.Operations
{
	internal class LDv : Operation
	{
		public LDv(int registerIndex, byte value)
		{
			RegisterIndex = registerIndex;
			Value = value;
		}

		public int RegisterIndex { get; set; }

		public byte Value { get; set; }

		public override void Execute(ICPU cpu)
		{
			cpu.V[RegisterIndex] = Value;
		}
	}

	internal class LDi : Operation
	{
		public LDi(ushort value)
		{
			Value = value;
		}

		public ushort Value { get; set; }

		public override void Execute(ICPU cpu)
		{
			cpu.I = Value;
		}
	}

	internal class LDr : Operation
	{
		public LDr(int targetRegisterIndex, int sourceregisterIndex)
		{
			SourceregisterIndex = sourceregisterIndex;
			TargetRegisterIndex = targetRegisterIndex;
		}

		public int SourceregisterIndex { get; set; }

		public int TargetRegisterIndex { get; set; }

		public override void Execute(ICPU cpu)
		{
			cpu.V[TargetRegisterIndex] = cpu.V[SourceregisterIndex];
		}
	}

	internal class LDldt : Operation
	{
		public LDldt(int targetRegisterIndex)
		{
			TargetRegisterIndex = targetRegisterIndex;
		}

		public int TargetRegisterIndex { get; set; }

		public override void Execute(ICPU cpu)
		{
			cpu.V[TargetRegisterIndex] = cpu.DT;
		}
	}

	internal class LDk : Operation
	{
		public LDk(int targetRegisterIndex)
		{
			TargetRegisterIndex = targetRegisterIndex;
		}

		public int TargetRegisterIndex { get; set; }

		public override void Execute(ICPU cpu)
		{
			//TODO: this should not block the whole emulation (Timers, exit, etc.)
			var key = cpu.Bus.KeyPad.WaitKey();
			cpu.V[TargetRegisterIndex] = (byte)key;
		}
	}

	internal class LDsdt : Operation
	{
		public LDsdt(int sourceRegisterIndex)
		{
			SourceRegisterIndex = sourceRegisterIndex;
		}

		public int SourceRegisterIndex { get; set; }

		public override void Execute(ICPU cpu)
		{
			cpu.DT = cpu.V[SourceRegisterIndex];
		}
	}

	internal class LDsst : Operation
	{
		public LDsst(int sourceRegisterIndex)
		{
			SourceRegisterIndex = sourceRegisterIndex;
		}

		public int SourceRegisterIndex { get; set; }

		public override void Execute(ICPU cpu)
		{
			cpu.ST = cpu.V[SourceRegisterIndex];
		}
	}

	internal class LDf : Operation
	{
		public LDf(int sourceRegisterIndex)
		{
			SourceRegisterIndex = sourceRegisterIndex;
		}

		public int SourceRegisterIndex { get; set; }

		public override void Execute(ICPU cpu)
		{
			throw new NotImplementedException();
		}
	}

	internal class LDb : Operation
	{
		public LDb(int sourceRegisterIndex)
		{
			SourceRegisterIndex = sourceRegisterIndex;
		}

		public int SourceRegisterIndex { get; set; }

		public override void Execute(ICPU cpu)
		{
			int value = cpu.V[SourceRegisterIndex];
			int digit = value / 100;
			cpu.Bus.RAM[cpu.I] = (byte)digit;
			value = value - digit * 100;
			digit = value / 10;
			cpu.Bus.RAM[(ushort)(cpu.I + 1)] = (byte)digit;
			value = value - digit * 10;
			cpu.Bus.RAM[(ushort)(cpu.I + 2)] = (byte)value;
		}
	}

	internal class LDsmr : Operation
	{
		public LDsmr(int registerIndex)
		{
			RegisterIndex = registerIndex;
		}

		public int RegisterIndex { get; set; }

		public override void Execute(ICPU cpu)
		{
			for (int i = 0; i <= RegisterIndex; ++i)
			{
				cpu.Bus.RAM[(ushort)(cpu.I + i)] = cpu.V[i];
			}
		}
	}

	internal class LDlmr : Operation
	{
		public LDlmr(int registerIndex)
		{
			RegisterIndex = registerIndex;
		}

		public int RegisterIndex { get; set; }

		public override void Execute(ICPU cpu)
		{
			for (int i = 0; i <= RegisterIndex; ++i)
			{
				cpu.V[i] = cpu.Bus.RAM[(ushort)(cpu.I + i)];
			}
		}
	}
}
